WICONF.initZTree = {
    nNodeH: 30,// 每个节点的高度，与 css 中 .ztree li>a 一致
    view: {
        showLine: true,// 显示树线
        showIcon: true// 显示节点图标
    }
};
/**
 * @widoc $.fn.initZTree
 * @namespace comp
 * @des 初始化 tree。用于初始化的元素必须为 &lt;ul&gt;&lt;/ul&gt;，如果需要在过滤时将匹配节点滚动至可视区，需要令滚动条出现在初始化元素上（即：有一定高度，且 overflow-y:auto）。此组件在 zTree 基础上扩展，未做扩展的接口请查看：http://www.treejs.cn/v3/api.php
 * 组件配置，在使用组件前调用：
 <pre class="des-conf"> $.setWiConf('initZTree',{
    nNodeH:30,// 每个节点的高度，与 css 种 .ztree li>a 一致
    view:{
        showLine:true,// 显示树线
        showIcon:true// 显示节点图标
    }
 });</pre>
 * @type function
 * @demo initZTree/demo0 静态树
 * @demo initZTree/demo1 动态树、过滤
 * @param {object} _opts 用户配置
 * @param {boolean=} _opts.multi 是否多选，默认为 false。
 * @param {Array=} _opts.data 树数据源，若定义，则 opts.url 及 opts.urlArgs 失效
 * @param {string=} _opts.url 树数据源请求地址，若定义了 opts.data，此属性失效
 * @param {object=} _opts.urlArgs 树数据源请求时向后台传递的参数，若定义了 opts.data，此属性失效
 * @param {Array=} _opts.selParams 指定加载完成后选中项条件[key,value]，复选时，value 支持数组
 * @param {object} _opts.filterEl 过滤文本框对象
 * @param {object} _opts.filter 过滤功能配置，仅当定义了 filterEl 时生效
 * @param {string|Array} _opts.filter.fields 参与过滤的列的属性名，默认与 opts.key.name 相同
 * @param {boolean} _opts.filter.pySupport 是否支持拼音首字过滤，开启过滤时，默认为 true
 * @param {boolean} _opts.filter.hide 是否隐藏不符合过滤条件的节点，默认为 true，复选时此属性无效
 * @param {boolean=} _opts.initSelect 初始化时若没有选中项，是否选中第一个可选项，默认为 true，复选时失效。
 * @param {function=} _opts.selector(node) 选择时的过滤条件，节点可选时返回 true。
 * @param {object=} _opts.async ztree 初始化时的 setting.async
 * @param {object=} _opts.check ztree 初始化时的 setting.check。默认值：
 *        enable 复选为 true，单选为 false（非特殊情况请勿覆写）
 *        chkboxType 为 { "Y": "ps", "N": "ps" }，即级联选中（可覆写）
 * @param {object=} _opts.callback ztree 初始化时的 setting.callback
 * @param {object=} _opts.edit ztree 初始化时的 setting.edit
 * @param {object=} _opts.view ztree 初始化时的 setting.view。默认值：
 *        fontCss: function (treeId, treeNode) {// 高亮（过滤）
 *            return (!!treeNode.__hlight) ? {color: "#A60000", "font-weight": "bold"} : {color: "#333", "font-weight": "normal"};
 *        }
 *        selectedMulti: 复选为true，单选为false（非特殊情况请勿覆写）
 * @param {object=} _opts.key ztree 初始化时的 setting.data.key
 * @param {object=} _opts.simpleData ztree 初始化时的 setting.data.simpleData（opts.simple.enable: false 使用/不使用 简单数据模式，默认为树形结构）
 * @param {function=} _opts.afterInit 完成初始化后调用的方法
 *        treeObj {object} $.fn.initZTree() 返回的接口
 * @param {function=} _opts.onSelect 设置选中项后的回调函数。触发时机：初始化完成后，obj.setSel(selParams,clear)，onClick(单选)/onCheck(复选)
 *        selnodes {object|Array} 单选时为选中节点，复选时为选中节点数组
 *        treeObj {object} $.fn.initZTree() 返回的接口
 * @return {object|undefined} obj 接口对象
 * @rtn {object} obj.treeObj ztree初始化完成后获得的树对象
 * @rtn {string} obj.treeId 当前树 id
 * @rtn {object} obj.setting ztree 初始化时使用的 setting
 * @rtn {function} obj.setSel(selParams,clear) 设置选中项
 *        selParams {Array=} 指定要选中的项的条件[key,value]，复选时 value 支持数组
 *        clear {boolean=} 是否先清空原有选中项，默认为 true
 * @rtn {function} obj.reset(url,urlArgs,selParams): 重新请求数据源,若未定义参数，则使用初始化时的配置重新请求
 *        url {string=} 重新请求的地址，未定义则使用上一次指定的url
 *        urlArgs {object=} 重新请求时向后台传递的参数
 *        selParams {Array=} 重新请求时，加载完成后选中项条件[key,value]
 * @rtn {function} obj.reset(data,selParams): 重置数据源，若未定义参数，则使用初始化时的配置重新初始化树
 *        data{Array=} 重置的树数据源
 *        obj.reset(undefined,selParams) 与 obj.reset(undefined,undefined,,selParams) 相当于 obj.setSel(selParams,true)
 * @rtn {function} obj.getNodeById(tId) 根据 tid 获取节点，返回值 {object} 节点数据
 *        tId {string} ztree 中的节点标识 tid
 * @rtn {function} obj.getSelectedNodes() 单选返回选中项，无则返回 undefined；复选返回选中项数组（含半选），无则返回[]
 * @rtn {function} obj.getSelectedNodesNoHalf() 单选同obj.getSelectedNodes()；复选返回选中项数组（不含半选），无则返回[]
 */
$.fn.initZTree = function(_opts){
    //if(!_opts || (!_opts.data && !_opts.url)) return;// 未定义数据源且未定义数据源请求url
    var el = $(this);
    var treeId = el.attr('id'); // 树控件 id
    if(!treeId){
        treeId = $.generateGuid('zTree');
        el.attr('id', treeId);
    }
    el.addClass('ztree');
    var opts = $.extend({
        multi: false// 默认单选
        //        ,data:[]
        //        ,url:''
        //        ,urlArgs:{}
        //        ,selParams:[key,val]
        , initSelect: true
        //        ,filterEl
        , filter: {}
        //        ,selector:function(node)
        //        ,async,check,callback,edit,view,key,simpleData
        //        ,afterInit:function(_returnObj)
        //        ,onSelect:function(selnodes,_returnObj)
    }, _opts);
    var _conf = {// 辅助参数
        filterRemains: 0// 搜索关键字时修改节点高亮属性及展开父节点（会造成延时）的剩余操作总数
        , scrollNode: undefined// 要移入视图的节点
        , filtering: false
        , filterList: []// 高亮节点列表 / 隐藏节点列表
        , initing: false// 标记是否处于初始化过程，避免初始化时展开选中节点的祖先的操作进入 _afterFilterOpt 导致无法完成初始化及触发用户回调
        , selChanging: false// 是否正在调整选中项，_afterFilterOpt 中触发 onSelect 事件
    };
    var _treeObj // ztree 初始化完成后返回的接口对象，在 initTree() 中赋值
        , arrayNodes // 数组型节点数据源，在 initTree() 中赋值
        , multi = _opts.multi
        , selNode;// 单选时初始化时选中的节点，若存在将在 _afterFilterOpt 中触发点击
    var _filterObj;
    var _returnObj = {
        'treeId': treeId
        // 根据用户配置初始化 zTree setting
        , 'setting': (function(opts, multi){
            var setting = {
                async: opts.async || {},
                callback: opts.callback || {},
                check: $.extend({
                    enable: multi // 是否显示复选框
                    , chkboxType: {"Y": "ps", "N": "ps"} // 禁止级联 TODO 此配置实际为允许级联
                }, opts.check),
                data: {
                    key: opts.key,
                    simpleData: opts.simpleData
                },
                edit: opts.edit || {},
                view: $.extend({// 根据 treeNode.__hlight 设置节点样式
                    fontCss: function(treeId, treeNode){
                        return (!!treeNode.__hlight) ? {color: "#A60000", "font-weight": "bold"} : {
                            color: "#333",
                            "font-weight": "normal"
                        };
                    }
                    , selectedMulti: multi
                }, WICONF.initZTree.view, opts.view)
            };
            /* 封装默认的回调方法 */
            // 因为聚焦元素时会展开节点（异步），所以要加入默认操作
            setting.callback.onExpand = (function(_onExtend){
                return function(event, treeId, node){
                    if(_conf.filtering){// 过滤时展开完成，操作总数-1，不执行用户操作
                        _conf.filterRemains--;
                        _afterFilterOpt();
                    }
                    else{
                        _onExtend(event, treeId, node);
                    }
                }
            })(setting.callback.onCheck || $.noop);
            // 设置选中项后的回调函数
            if(opts.onSelect){
                if(multi){// 复选
                    setting.callback.onCheck = (function(_onCheck){
                        return function(event, treeId, treeNode){
                            _onCheck(event, treeId, treeNode);
                            opts.onSelect(_getSelectedNodesNoHalf(), _returnObj);
                        }
                    })(setting.callback.onCheck || $.noop);
                }
                else{
                    setting.callback.onClick = (function(_onClick){
                        return function(event, treeId, treeNode, clickFlag){
                            _onClick(event, treeId, treeNode, clickFlag);
                            opts.onSelect(_getSelectedNodesNoHalf(), _returnObj);
                        }
                    })(setting.callback.onClick || $.noop);
                }
            }
            // 单选定义了选择时的过滤条件（复选在初始化时对数据源 nocheck 属性设置隐藏复选框）
            if(!multi && opts.selector){
                setting.callback.beforeClick = (function(_beforeClick){
                    return _beforeClick ? function(treeId, node, flag){
                        return opts.selector(node) && _beforeClick(treeId, node, flag);
                    } : function(treeId, node, flag){
                        return opts.selector(node);
                    }
                })(setting.callback.beforeClick);
            }
            return setting;
        })(opts, multi)
    };
    // 过滤完成后，显示结果
    var _filterNodes = function(selNodes, keyword){
        if(!_filterObj) return;// 过滤组件初始化过程中，关键字为 ''，不执行过滤，否则将清空点击状态
        _conf.filtering = true;
        if(opts.filter.hide){
            if(!_conf.initing){// 非初始化的过滤才将原选中项取消，否则导致 _filterObj.resetData() 时将默认选中项清空
                _treeObj.cancelSelectedNode(_getSelectedNodes());
            }
            showFilter(selNodes, keyword);// 显示过滤结果
        }
        else{
            highlight(_conf.filterList, false);
            if(keyword){
                _conf.filterList = selNodes;
                selNodes && selNodes.length && highlight(selNodes, true);
            }
        }
    };
    // 初始化过滤支持，必须在数据加载完成后调用
    var _initFilter = function(){
        if(!opts.filterEl) return;
        if(_filterObj){// 更新过滤组件数据源
            _filterObj.setData(arrayNodes);
        }
        else{
            var filter = opts.filter;
            if(!filter.fields && opts.key && opts.key.name) filter.fields = opts.key.name;
            filter.hide = !opts.multi && filter.hide != false;// 只有单选且允许隐藏时为 true，多选禁止隐藏
            var filterOpts = {
                fields: filter.fields
                , data: arrayNodes
                , afterFilter: _filterNodes
            };
            if(filter.pySupport != undefined) filterOpts.pySupport = filter.pySupport;
            _filterObj = opts.filterEl.initFilter(filterOpts);
        }
    };
    var _scrollTo = function(node){
        if(node){
            _conf.filtering = true;
            _conf.filterRemains++;
            _conf.scrollNode = node;
            _expandAncestors(node);
        }
        else if(_conf.scrollNode){// 不定义滚动项时默认滚动到 _conf.scrollNode
            var nodeId = _conf.scrollNode.tId.replace(/\s/g, '_');
            var _treeDom = document.getElementById(treeId);
            var _treeBCR = _treeDom.getBoundingClientRect(),
                _nodeBCR = document.getElementById(nodeId).getBoundingClientRect(),
                nNodeH = WICONF.initZTree.nNodeH;
            if(_nodeBCR.top + nNodeH > _treeBCR.bottom){
                _treeDom.scrollTop += _nodeBCR.top + nNodeH - _treeBCR.bottom;
            }
            else if(_nodeBCR.top < _treeBCR.top){
                _treeDom.scrollTop += _nodeBCR.top - _treeBCR.top;
            }
            //document.getElementById(nodeId).scrollIntoView();// 此方法会将过滤节点滚动到最顶，即时它原本就在可视区。网上说安卓中的浏览器对此方法支持不好
            _conf.scrollNode = undefined;
        }
    };
    /**
     * 搜索到的所有节点的祖先节点及属性修改完成后，将 _conf.scrollNode 对应的节点滚入视图
     */
    var _afterFilterOpt = function(){
        if(_conf.filtering && _conf.filterRemains == 0){// 所有节点操作完成，使第一个高亮节点进入视图
            if(_conf.initing){// 在初始化过程中
                _initFilter();
                _conf.initing = false;// 必须在 _initFilter 之后，否则会被清空选中项
                opts.afterInit && opts.afterInit(_returnObj);
            }
            if(_conf.selChanging){// 正在修改选中项
                _conf.selChanging = false;
                if(selNode){// selNode 存在：单选且有选中项
                    $('#' + selNode.tId + '_a').click();// 点击要选中的项，onClick 事件中会触发 onSelect
                    selNode = undefined;
                }
                else{// 复选或无选中项
                    opts.onSelect && opts.onSelect(_getSelectedNodesNoHalf(), _returnObj);
                }
            }
            _scrollTo();
            _conf.filtering = false;
        }
    };
    /**
     * 选中指定项
     * @param selParams {Array=} 指定加载完成后选中项条件[key,value]，复选时，value支持数组
     * @param clear {boolean=} 是否要清除原有选中项，默认为 true，初始化时失效
     * @private
     */
    var _setSel = function(selParams, clear){
        clear = clear != false;
        var len = arrayNodes.length;
        _conf.scrollNode = undefined;
        _conf.filtering = true;
        _conf.selChanging = true;// 正在改变选中值
        if(len > 0){
            var key, val;
            if(selParams){// 默认选中项
                key = selParams[0];
                val = selParams[1];
            }
            var firstEnableNode;// 第一个可选节点
            var i, node;// 循环临时变量
            var selectedNodes = _getSelectedNodes();// 原来选中的项
            if(multi){
                if(key && Object.prototype.toString.call(val) !== '[object Array]'){// 复选时，将 val 统一为数组
                    val = [val];
                }
                if(clear){// 清除原有选中项
                    for(i = 0; i < selectedNodes.length; i++){
                        _treeObj.checkNode(selectedNodes[i], false);
                    }
                }
                if(_conf.initing){
                    for(i = 0; i < len; i++){// 根据 selParams 及 selector 勾选，并隐藏不可选的节点的勾选框
                        node = arrayNodes[i];
                        if(!opts.selector || opts.selector(node)){// 未定义 selector，或符合 selector
                            if(!firstEnableNode) firstEnableNode = node;
                            if(key && val.indexOf(node[key]) > -1){// 符合 selParams
                                _treeObj.checkNode(node, true, false);// 因为可能存在由数据源决定的选中状态，故最后统一处理级联
                            }
                        }
                        else{// 不可选的节点隐藏复选框
                            node['nocheck'] = true;
                            _treeObj.updateNode(node);
                        }
                    }
                    // 统一处理级联，根据选中的项设置级联父子节点
                    if(_returnObj.setting.check.chkboxType['Y']){// 允许级联，手动选中各项，以触发级联选中
                        selectedNodes = _returnObj.getSelectedNodes();// 获取当前勾选项集合（初始化时均未级联）
                        for(i = 0, len = selectedNodes.length; i < len; i++){
                            _treeObj.checkNode(selectedNodes[i], true, true);
                        }
                    }
                }
                else{
                    for(i = 0; i < len; i++){// 根据 selParams 及 selector 勾选，并隐藏不可选的节点的勾选框
                        node = arrayNodes[i];
                        if(!opts.selector || opts.selector(node)){// 未定义 selector，或符合 selector
                            if(key && val.indexOf(node[key]) > -1){// 符合 selParams
                                _treeObj.checkNode(node, true, true);
                            }
                        }
                    }
                }
                selectedNodes = _getSelectedNodesNoHalf();// 获取最终的选中集合（不含半选项）
                len = selectedNodes.length;
                if(len){// 有选中项
                    _conf.filterRemains += len;
                    for(i = 0; i < len; i++){// 展开所有选中项
                        node = selectedNodes[i];
                        !_conf.scrollNode && (_conf.scrollNode = node);// 记录第一项过滤层级的节点（非半选项）
                        _expandAncestors(node);
                    }
                }
                else if(_conf.initing && firstEnableNode){// 展开第一个可选项
                    _conf.filterRemains++;
                    _conf.scrollNode = firstEnableNode;
                    _expandAncestors(firstEnableNode);
                }
            }
            else{
                if(clear && selectedNodes){// 清除原有选中项
                    _treeObj.cancelSelectedNode(selectedNodes);
                }
                for(i = 0; i < len; i++){// 根据 selParams 及 selector 勾选
                    node = arrayNodes[i];
                    if(!opts.selector || opts.selector(node)){// 未定义 selector，或符合 selector
                        if(!firstEnableNode) firstEnableNode = node;
                        if(key && val == node[key]){// 符合 selParams
                            selNode = node;
                            break;
                        }
                    }
                }
                if(_conf.initing && !selNode){// 初始化且没有选中项，将第一个可选项滚入视图
                    _conf.scrollNode = firstEnableNode;
                    if(opts.initSelect){// 选中第一个可选项，在 _afterFilterOpt 中触发点击。
                        selNode = firstEnableNode;
                    }
                }
                else{
                    _conf.scrollNode = selNode;
                }
                if(_conf.scrollNode){
                    _conf.filterRemains++;
                    _expandAncestors(_conf.scrollNode);
                }
            }
        }
        _afterFilterOpt();
    };
    var _initTree = function(data){
        if(typeof(data) == 'string') data = JSON.parse(data);
        _treeObj && _treeObj.destroy(treeId);
        _conf.filterList.length = 0;
        _treeObj = _returnObj.treeObj = $.fn.zTree.init($("#" + treeId), _returnObj.setting, data);
        arrayNodes = _treeObj.transformToArray(_treeObj.getNodes());
        _conf.initing = true;// 标识正在进行初始化，完成后在 _afterFilterOpt() 中处理初始化回调
        _setSel(opts.selParams, false);
    };
    var _init = function(){
        if(opts.url){
            opts.data = undefined;
            $.ajax({// 请求数据初始化树
                type: "post",
                dataType: "json",
                url: opts.url,
                data: opts.urlArgs || {},
                success: function(result){
                    if(!result.success){
                        result.msg && $.showAlert(result['msg']);
                        return;
                    }
                    _initTree(result.data);
                }
            });
        }
        else{
            if(!opts.data){
                opts.data = [];
            }
            opts.url = undefined;
            _initTree(opts.data);
        }
        //if(opts.data){
        //    opts.url=undefined;
        //    _initTree(opts.data);
        //}else{
        //    $.ajax({// 请求数据初始化树
        //        type: "post",
        //        dataType: "json",
        //        url: opts.url,
        //        data: opts.urlArgs || {},
        //        success: function (result) {
        //            if(!result.success){
        //                result.msg && $.showAlert(result['msg']);
        //                return;
        //            }
        //            _initTree(result.data);
        //        }
        //    });
        //}
    };

    /**
     * 展开指定节点的所有祖先
     * @param node {object} 树中的节点对象
     * @private
     */
    function _expandAncestors(node){
        var level = node['level']
            , pnode = node;
        while(level > 0){
            pnode = pnode.getParentNode();
            if(pnode.open == false){
                _conf.filterRemains++;// 新增一个父节点操作
                /* 展开父节点，完成后触发 onExpand */
                _treeObj.expandNode(pnode, true, false, false, true);
                break;// expandNode 操作会展开父级节点
            }
            level--;
        }
        _conf.filterRemains--;
        _afterFilterOpt();
    }

    function showFilter(nodeList, keyword){
        _conf.filtering = true;
        var nodes = _treeObj.getNodes();
        var childrenField = _treeObj.setting.data.key.children;
        /**
         * @return {boolean} nodes 中是否存在需要显示的节点
         */
        var chargeNodes = function(nodes){
            if(!nodes || !nodes.length) return false;
            var show = false;
            for(var i = 0, node, children, nodeShow = false; i < nodes.length; i++){
                node = nodes[i];
                children = node[childrenField];
                nodeShow = chargeNodes(children);// 先判断子孙中是否有匹配的节点
                if(!keyword){
                    node.__hlight = false;
                    nodeShow = true;
                }
                else if(nodeList.indexOf(node) > -1){// 当前节点匹配
                    node.__hlight = true;
                    nodeShow = true;
                }
                else{
                    node.__hlight = false;
                }
                if(nodeShow){
                    _treeObj.showNode(node);
                }
                else{
                    _treeObj.hideNode(node);
                    _conf.filterList.push(node);
                }
                _treeObj.updateNode(node);// 更新节点状态
                show = show || nodeShow;
            }
            return show;// 没有匹配的子节点
        };
        _conf.filterList.length = 0;
        chargeNodes(nodes);
        if(keyword) _treeObj.expandAll(true);// 搜索结果全部展开
    }

    function highlight(nodeList, highlight){
        if(!nodeList) return;
        var i, node;// 循环临时变量
        if(highlight){// 开启高亮
            var scrollNode;
            _conf.filterRemains = nodeList.length;// 等待完成的节点操作总数，初始时为搜索到的节点总数（展开节点会产生延迟）
            for(i = 0; i < nodeList.length; i++){
                node = nodeList[i];
                !scrollNode && (!multi || !node.getCheckStatus.half) && (scrollNode = _conf.scrollNode = node);// 记录第一项过滤层级的节点（非半选项）
                node.__hlight = true;
                _treeObj.updateNode(node);
                _expandAncestors(node);// 展开所有高亮节点的祖先
            }
        }
        else{// 取消高亮
            for(i = 0; i < nodeList.length; i++){
                node = nodeList[i];
                node.__hlight = false;
                _treeObj.updateNode(node);
            }
        }
    }

    /**
     * 更新数据源，重新初始化树 reset(url,urlArgs,selParams);reset(data,selParams)
     * @param url {string|Array=}
     * @param urlArgs {object|Array=}
     * @param selParams {Array=} 默认选中项条件 [key,value]
     */
    var _reset = function(url, urlArgs, selParams){
        var _data, _url, _urlArgs, _selParams;
        if(typeof url == 'string'){
            _url = url;
            opts.urlArgs = undefined;// 重置数据源，原默认选中项置空
            opts.selParams = undefined;// 重置数据源，原默认选中项置空
        }
        else if(Object.prototype.toString.call(url) === '[object Array]'){
            _data = url;
            opts.selParams = undefined;// 重置数据源，原默认选中项置空
        }
        if(Object.prototype.toString.call(urlArgs) === '[object Array]'){
            _selParams = urlArgs;
        }
        else if(urlArgs){
            _urlArgs = urlArgs
        }
        if(selParams) _selParams = selParams;
        if(_urlArgs) opts.urlArgs = _urlArgs;
        if(_selParams) opts.selParams = _selParams;
        if(_data){
            opts.data = _data;
            opts.url = undefined;
        }
        else if(_url){
            opts.url = _url;
            opts.data = undefined;
        }
        _data || _url || _urlArgs ? _init() : _setSel(opts.selParams, true);
    };
    var _getSelectedNodes = function(){
        return multi ? _treeObj.getCheckedNodes(true) : _treeObj.getSelectedNodes(true)[0];
    };
    var _getSelectedNodesNoHalf = function(){
        var selNodes = _getSelectedNodes();
        if(multi){
            var result = [];
            for(var i = 0, len = selNodes.length, node; i < len; i++){
                node = selNodes[i];
                !node.getCheckStatus().half && result.push(node);// 只保留非半选的节点
            }
            return result;
        }
        return selNodes;
    };
    _returnObj.setSel = _setSel;
    _returnObj.reset = _reset;// 更新数据源
    _returnObj.getNodeById = function(tId){// _treeObj 直接获取可能还未完成初始化
        return _treeObj.getNodeByTId(tId);
    };
    _returnObj.getSelectedNodes = _getSelectedNodes;// 复选时，含半选项
    _returnObj.getSelectedNodesNoHalf = _getSelectedNodesNoHalf;// 复选时，不含半选项
    _init();
    return _returnObj;
};